using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Reflection;
using XPathParser;
using System.Diagnostics;
using System.Xml.XPath;

namespace CodeGenerationPipelineInterface
{
	[DebuggerDisplay("{ DebugInfo }")]
    public class InterfaceDocument : IXmlDocument
    {
        private XDocument internalDocument;
        private XmlSchemaSet schemaSet;
        private List<string> messages;
        private XmlNamespaceManager namespaceManager;
        private XNamespace elementNamespace;
        private bool hasBeenReset;
        private List<ElementBuild> elementBuilds;

        public InterfaceDocument()
        {
            messages = new List<string>();
           elementBuilds = new List<ElementBuild>();
        }

        public List<ElementBuild> ElementBuilds
        {
            get
            {
                return elementBuilds;
            }
        }
        public XNamespace ElementNamespace
        {
            get
            {
                return elementNamespace;
            }
        }

        public XmlNamespaceManager NamespaceManager 
        { 
            get
            {
                return namespaceManager;
            }
        }

        public void Reset()
        {
            namespaceManager = new XmlNamespaceManager(new NameTable());
            hasBeenReset = true;
        }

        public void AddNamespace(string prefix, string uri)
        {
            namespaceManager.AddNamespace(prefix, uri);
        }

        public void AddElementNamespace(string prefix, string uri)
        {
            namespaceManager.AddNamespace(prefix, uri);
            elementNamespace = uri;
        }

        public IXmlDocument Document 
        { 
            get
            {
                return this;
            }

            set
            {
                throw new NotImplementedException();
            }
        }

        public List<string> Messages
        {
            get
            {
                return messages;
            }
        }

        public XDocument InternalDocument
        {
            get 
            {
                return internalDocument;
            }
        }

        public void Open(Stream stream)
        {
            internalDocument = XDocument.Load(stream);
            ProcessOpen();
        }

        public void Open(string uri)
        {
            internalDocument = XDocument.Load(uri);
            ProcessOpen();
        }

        public void Open(TextReader textReader)
        {
            internalDocument = XDocument.Load(textReader);
            ProcessOpen();
        }

        public void Open(XmlReader reader)
        {
            internalDocument = XDocument.Load(reader);
            ProcessOpen();
        }

        public void Open(Stream stream, LoadOptions options)
        {
            internalDocument = XDocument.Load(stream, options);
            ProcessOpen();
        }

        public void Open(string uri, LoadOptions options)
        {
            internalDocument = XDocument.Load(uri, options);
            ProcessOpen();
        }

        public void Open(TextReader textReader, LoadOptions options)
        {
            internalDocument = XDocument.Load(textReader, options);
            ProcessOpen();
        }

        public void Open(XmlReader reader, LoadOptions options)
        {
            internalDocument = XDocument.Load(reader, options);
            ProcessOpen();
        }

        public void Parse(string text)
        {
            internalDocument = XDocument.Parse(text);
            ProcessOpen();
        }

        public void Parse(string text, LoadOptions options)
        {
            internalDocument = XDocument.Parse(text, options);
            ProcessOpen();
        }

        public void Save(Stream stream, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(stream);
            }
        }

        public void Save(string fileName, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(fileName);
            }
        }

        public void Save(TextWriter textWriter, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(textWriter);
            }
        }

        public void Save(XmlWriter writer, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(writer);
            }
        }

        public void Save(Stream stream, SaveOptions options, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(stream, options);
            }
        }

        public void Save(string fileName, SaveOptions options, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(fileName, options);
            }
        }

        public void Save(TextWriter textWriter, SaveOptions options, ValidationEventHandler handler)
        {
            if (hasBeenReset)
            {
                ProcessSave();
            }

            if (this.Validate(this, handler))
            {
                internalDocument.Save(textWriter, options);
            }
        }

        public void AddSchema(Stream stream)
        {
            if (schemaSet == null)
            {
                schemaSet = new XmlSchemaSet();
            }

            var schema = XmlSchema.Read(stream, (o, e) =>
            {
                messages.Add(e.Message);
            });

            schemaSet.Add(schema);

            schemaSet.Compile();
        }

        public void AddSchema(string uri)
        {
            var schemaStream = System.IO.File.OpenRead(uri);

            this.AddSchema(schemaStream);
        }

        private XmlNamespaceManager CreateNamespaceManager()
        {
            var manager = new XmlNamespaceManager(new NameTable());

            foreach (XAttribute attribute in internalDocument.Root.XPathSelectElement("/*").Attributes())
            {
                if (attribute.Name.NamespaceName == "http://www.w3.org/2000/xmlns/")
                {
                    manager.AddNamespace(attribute.Name.LocalName, attribute.Value);
                }
            }

            return manager;
        }

        public void ProcessOpen()
        {
            namespaceManager = this.CreateNamespaceManager();

            this.Read(internalDocument.Root);
        }

        public void ProcessSave()
        {
            internalDocument = new XDocument();

            this.Write(internalDocument, namespaceManager);
        }

        public string ElementName
        {
            get 
            {
                return "InterfaceDocument";
            }
        }

        public string DebugInfo
        {
            get 
            {
                return "InterfaceDocument";
            }
        }

        public void Read(XNode node)
        {
            var elementNode = (XElement) node;
            var prefix = elementNode.GetPrefixOfNamespace(elementNode.Name.Namespace);

            // ElementBuild

            foreach (var element in internalDocument.XPathSelectElements(prefix + ":ElementBuild", this.namespaceManager))
            {
                var elementBuild = new ElementBuild(this.Document);

                elementBuild.Read(element);

                this.elementBuilds.Add(elementBuild);
            }
        }

        public void Write(XNode parentNode, XNamespace elementNamespace)
        {
            this.Write(internalDocument, namespaceManager);
        }

        public void Write(XDocument document, XmlNamespaceManager namespaces)
        {
            foreach (ElementBuild elementBuilds in this.ElementBuilds)
            {
                elementBuilds.Write(document, namespaces);
            }
        }

        public bool Validate(object sender, ValidationEventHandler handler)
        {
            var valid = true;

            if (internalDocument != null)
            {
                internalDocument.Validate(schemaSet, (sender2, e) =>
                {
                    messages.Add(e.Message);
                    valid = false;
                });
            }

            return valid;
        }
    }
}
